package com.laysonx.easy.invoke.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.laysonx.easy.invoke.support.*;
import com.laysonx.easy.invoke.util.SpringContextUtil;
import com.laysonx.easy.invoke.util.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import static org.springframework.web.bind.annotation.RequestMethod.POST;

@Controller
@Slf4j
public class EasyInvokeController {

//    @Resource
//    ResourcePool resourcePool;

    @Resource
    InvokeMonitor invokeMonitor;

    /**
     * 进入首页
     *
     * @author: laysonx
     * @date: 2019/11/7 18:10
     * @return: org.springframework.web.servlet.ModelAndView
     */
    @RequestMapping("/easy-invoke")
    public ModelAndView index() {

        log.info("进入首页");
        ModelAndView mv = new ModelAndView();
        mv.setViewName("/easy-invoke/index.html");
        mv.addObject("domain", invokeMonitor.getDomain());
        return mv;
    }

    /**
     * 查询目标 bean 下相关数据
     *
     * @param beanName
     * @author: laysonx
     * @date: 2019/11/13 11:51
     * @return: org.springframework.web.servlet.ModelAndView
     */
    @RequestMapping("/easy-invoke/search/{beanName}")
    public ModelAndView search(@PathVariable String beanName) {

        ModelAndView mv = new ModelAndView();
        mv.setViewName("/easy-invoke/index.html");
        mv.addObject("domain", invokeMonitor.getDomain());

        try {
            Object targetBean = SpringContextUtil.getBean(beanName);
            if (targetBean != null) {

                List<com.laysonx.easy.invoke.support.Resource> resourceList = new ArrayList<>();
                Class targetClass = targetBean.getClass();

                Method[] methods = targetClass.getMethods();
                if (methods != null && methods.length != 0) {
                    for (Method method : methods) {

                        com.laysonx.easy.invoke.support.Resource resource = new com.laysonx.easy.invoke.support.Resource();
                        resource.setPath(targetClass.toString());
                        resource.setName(beanName);
                        // TODO 排除object方法
                        com.laysonx.easy.invoke.support.Method resourceMethod = new com.laysonx.easy.invoke.support.Method();
                        resourceMethod.setName(method.getName());
                        Class<?> returnType = method.getReturnType();
                        resourceMethod.setReturnType(returnType);

                        List<Param> params = new ArrayList<>();

                        Class<?>[] parameterTypes = method.getParameterTypes();
                        if (parameterTypes != null && parameterTypes.length != 0) {
                            for (Class<?> parameterType : parameterTypes) {
                                Param param = new Param();
                                param.setType(parameterType);
                                params.add(param);
                            }
                        }

                        resourceMethod.setParams(params);

                        resource.setMethod(resourceMethod);

                        String resourceCodeMaterial = JSONObject.toJSONString(resource); // parse 为JSONObject 添加额外数据
                        String code = TokenUtil.createToken(resourceCodeMaterial);
                        resource.setCode(code);

                        resourceList.add(resource);
                    }
                }
                mv.addObject("beanName", beanName);
                mv.addObject("resourceList", resourceList);
            }
        } catch (Exception e) {
            mv.addObject("exception", e.getMessage());
        }

        return mv;
    }

    /**
     * 准备invoke所需数据
     *
     * @author: laysonx
     * @date: 2019/11/7 18:11
     * @return: java.lang.Object
     */
    @RequestMapping("/easy-invoke/prepare/{ticket}")
    public ModelAndView prepareInvoke(@PathVariable String ticket) {

        System.out.println("### password " + ticket);

        // 校验密码
        // 生成带时间、次数、用户信息的token
        ModelAndView mv = new ModelAndView();
        mv.setViewName("/easy-invoke/invoke.html");
        mv.addObject("domain", invokeMonitor.getDomain());

        try {
            String resourceJson = TokenUtil.getTicket(ticket);
            JSONObject jsonObject = JSONObject.parseObject(resourceJson);

            InvokeBody invokeBody = new InvokeBody();
            invokeBody.setTarget(jsonObject.get("name").toString());
            invokeBody.setMethod(jsonObject.getJSONObject("method").getString("name"));

            List<String> paramsTypeList = new LinkedList<>();
            Object[] objects = jsonObject.getJSONObject("method").getJSONArray("params").toArray();
            if (objects != null && objects.length != 0) {
                for (Object param : objects) {
                    JSONObject paramOb = JSONObject.parseObject(param.toString());
                    paramsTypeList.add("\""+paramOb.getString("type") +"\"");
                }
            }
            invokeBody.setParamTypes(paramsTypeList);
            invokeBody.setName(ticket);

            mv.addObject("invokeBody", invokeBody);
        } catch (Exception e) {
            mv.addObject("exception", e.getMessage());
        }

        return mv;
    }


    /**
     * invoke 并返回结果
     *
     * @author: laysonx
     * @date: 2019/11/7 18:10
     * @return: org.springframework.web.servlet.ModelAniew
     */
    @RequestMapping(value = "/easy-invoke/invoke", method = POST)
    @ResponseBody
    public InvokeResult invokeMethod(@RequestBody InvokeBody invokeBody) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, IOException {

        // 校验 token
        // 符合调用频率则通过
        InvokeResult invokeResult = new InvokeResult();
        // 从 context 获取对象
        Object targetBean = SpringContextUtil.getBean(invokeBody.getTarget());
        Class targetClass = targetBean.getClass();

        Class<?>[] methodParamClass = invokeBody.getMethodParamClass();
        Method method;
        Object result;
        // 检查方法不匹配问题
        if (methodParamClass.length != 0) {
            method = targetClass.getMethod(invokeBody.getMethod(), methodParamClass);
            // 对象参数 反射创建
            result = method.invoke(targetBean, invokeBody.getMethodParam());
        } else {
            method = targetClass.getMethod(invokeBody.getMethod());
            result = method.invoke(targetBean);
        }

        if (method == null) {
            invokeResult.setBody("未找到Method");
            return invokeResult;
        }

        invokeResult.setBody(JSONObject.toJSONString(result));
        invokeMonitor.postProcessor(invokeBody, invokeResult);
        return invokeResult;
    }

}
